1 Introduction

The data utilized in this analysis is from a dataset called loan_default from Applied Analytics through Case Studies Using SAS and R, by Deepti Gupta, published by APress, ISBN - 978-1-4842-3525-6.

1.1 Purpose of Data Collection

The data for loan_default was collected to generate a model for predicting which customers are most likely to default.

1.2 Description of the Data Collection or Generation Process

The exact methods used to collect the data in loan_default are unfortunately unknown.

1.3 Sample Size and Number of Feature Variables

loan_default has a total sample size of 1,000, contains 15 feature variables, and has 1 label.

1.4 Itemized List of Feature Variables

The feature variables of loan_default are as follows:

  • Variable 1: Checking_Amount (Numeric)
  • Variable 2: Term (displayed in months (Numeric))
  • Variable 3: Credit_score (Numeric)
  • Variable 4: Gender (Categorical)
  • Variable 5: Marital_status (Categorical)
  • Variable 6: Car_loan (1- Own car loan, 0- Does not own car loan – Numeric)
  • Variable 7: Personal_loan (1- Own Personal loan, 0- Does not own Personal loan – Numeric)
  • Variable 8: Home_loan (1- Own Home loan, 0- Does not own Home loan – Numeric)
  • Variable 9: Education_loan (1- Own Education loan, 0- Does not own Education loan – Numeric)
  • Variable 10: Emp_status (Categorical)
  • Variable 11: Amount (Numeric)
  • Variable 12: Saving_amoun (Numeric)
  • Variable 13: Emp_duration (which is displayed in months (Numeric))
  • Variable 14: Age (which is displayed in years (Numeric))
  • Variable 15: No_of_credit_account (Numeric)

The label variable of loan_default is:

  • Default is the target Variable in dataset where 1 displays bank loan default and 0 displays bank loan non default.

The source for this information can be found in a PDF called BankLoanDefaultDataset-Description.pdf

2 Working Dataset

For the purposes of demonstrating imputation in the future, missing values were generated for each of Gender, Marital_status, Emp_status, Emp_duration, and Age to generate the working dataset loan_default_Mod01.

# ====================
# DATA INTAKE
# ====================

loan_default <- read.csv("https://raw.githubusercontent.com/saltwatersoup/MyCV/refs/heads/main/Data%20Sets/Loan%20Default%20Data/BankLoanDefaultDataset.csv")
# ====================
# DATA MODIFICATION
# Adding missing values
# ====================

# Copying loan_default
loan_default_Mod01 <- loan_default

# Creating random observation IDs and replacing the corresponding observations with missing

# loan_default_Mod01$Checking_Amount[sample(1:1000, 100, replace = FALSE)] <- NA
# loan_default_Mod01$Term[sample(1:1000, 100, replace = FALSE)] <- NA
# loan_default_Mod01$Credit_score[sample(1:1000, 100, replace = FALSE)] <- NA
loan_default_Mod01$Gender[sample(1:1000, 68, replace = FALSE)] <- NA
loan_default_Mod01$Marital_status[sample(1:1000, 87, replace = FALSE)] <- NA
# loan_default_Mod01$Car_loan[sample(1:1000, 100, replace = FALSE)] <- NA
# loan_default_Mod01$Personal_loan[sample(1:1000, 100, replace = FALSE)] <- NA
# loan_default_Mod01$Home_loan[sample(1:1000, 100, replace = FALSE)] <- NA
# loan_default_Mod01$Education_loan[sample(1:1000, 100, replace = FALSE)] <- NA
loan_default_Mod01$Emp_status[sample(1:1000, 136, replace = FALSE)] <- NA
# loan_default_Mod01$Amount[sample(1:1000, 100, replace = FALSE)] <- NA
# loan_default_Mod01$Saving_amoun[sample(1:1000, 100, replace = FALSE)] <- NA
loan_default_Mod01$Emp_duration[sample(1:1000, 201, replace = FALSE)] <- NA
loan_default_Mod01$Age[sample(1:1000, 159, replace = FALSE)] <- NA
# loan_default_Mod01$No_of_credit_account[sample(1:1000, 100, replace = FALSE)] <- NA

3 Distribution of Individual Features

Preparation for analysis calls for the following analytic tasks:

  1. Handling Missing Values
  2. Addressing Outliers

3.1 Handling Missing Values

loan_default_Mod01 has missing values, in the variables Gender, Marital_status, Emp_status, Emp_duration, and Age. To utilize observations with missing data, imputation will be performed prior to analysis.

# ====================
# PLOTTING MISSING VALUES
# ====================

# Generating data frame of missing values per variable
MissDatCounts <- data.frame(
  Variables = names(loan_default_Mod01),
  Missing = colSums(is.na(loan_default_Mod01))
)

# Generating interactive plot using plotly
Plot_MissingVals <- 
  # Taking a subset of MissDatCounts, so only entries with > 0 missing values will be displayed
  subset(MissDatCounts, Missing > 0) %>% 
  # Passing the subset to plot_ly
  plot_ly(
    x = ~Variables,
    y = ~Missing
  ) %>% 
  layout(
    title = list(
      text = "Missing Values per Variable"
    ),
    xaxis = list(
      title = "Variables with Missing Values",
      categoryorder = "trace"
    ),
    yaxis = list(
      title = "Number of Missing Values"
    )
  )

# Outputting plot
Plot_MissingVals

3.2 Addressing Outliers

Of particular note is the upper end of credit scores. Typical credit scores (FICO credit scores) range from 300 to 850. However, there are some less common scoring models that utilize different numerical ranges. The FICO NextGen ranges from 150 to 950. Though discontinued, VantageScore 1.0 and 2.0 ranged from 501 to 990.

There are some Credit_score values that exceed 990, but it is possible that those values may be the result of some rarer scoring models. If such a scoring model cannot be identified, then it may be worthwhile to convert credit score values over 990 to missing values.

# ====================
# PLOTTING ALL NUMERICAL NON-BINARY VARIABLES
# ====================

# Selecting only numeric variables
NumVars <- select(loan_default_Mod01, where(is.numeric))
# Selecting eliminating any binary variables
NumVars <- NumVars[!apply(NumVars, 2, function(x){all(match(x, c(0, 1, NA), nomatch = FALSE))})]

# Preparing a list of subplots
NumFig <- c()
# Using a for loop to generate a subplot per variable in NumVars
for(i in 1:length(names(NumVars))){
  NumFig[[i]] <- plot_ly(
    x = NumVars[[i]], 
    y = "", 
    type = "box",
    name = colnames(NumVars)[i]
  )
}

# Generating a plot that contains 8 subplots (one for each variable in NumVars) across 4 rows
Plot_NumVars <- 
  subplot(NumFig[[1]], NumFig[[2]], NumFig[[3]], NumFig[[4]], NumFig[[5]], NumFig[[6]], NumFig[[7]], NumFig[[8]], nrows = 4, margin = 0.05) %>% 
  layout(
    title = "Distributions of All Numerical Non-binary Variables",
    legend = list(
      title = list(text = "<b> Variable </b>"),
      bgcolor = "#E2E2E2",
      bordercolor = "#FFFFFF",
      borderwidth = 2
    )
  )

# Outputting plot
Plot_NumVars

4 Relationship Between Features

This section explores the potential relationships between feature variables, specifically:

  1. Checking_amount and Credit_score
  2. Marital_status and Emp_status
  3. Marital_status and Saving_amount
  4. Checking_amount, Credit_score, and Saving_amount

4.1 Checking_amount and Credit_score

It appears that higher checking amounts are associated with a narrower range of credit scores. Therefore, there may be an association between checking amount and credit score that is worth looking into.

# ====================
# PLOTTING `Checking_amount` AND `Credit_score`
# ====================

# Generating plot
FeatRel01 <- 
  plot_ly(
    data = loan_default_Mod01,
    x = ~Checking_amount,
    y = ~Credit_score
  ) %>% 
  layout(
    title = "Checking Amount and Credit Score",
    xaxis = list(title = "Checking Amount"),
    yaxis = list(title = "Credit Score")
  )

# Outputting plot
FeatRel01

4.2 Marital_status and Emp_status

For all marital statuses, unemployed outnumbers both employed and unknown. However, married-unemployed individuals outnumber every other category. Associations like this may prove useful for imputation of unknowns.

# ====================
# PLOTTING `Marital_status` AND `Emp_status`
# ====================

# Preparing plot data
MarStat_EmpStat <- 
  # Subsetting loan_default_Mod01 to just Marital_status and Saving_amount
  loan_default_Mod01[ , c("Marital_status", "Emp_status")] %>% 
  # Replacing missing values with the word "Unknown"
  replace_na(list(Marital_status = "Unknown", Emp_status = "Unknown")) %>%
  # Grouping data by Marital_status and Emp_status
  group_by(Marital_status, Emp_status) %>% 
  # Counting the number of entries in each subgroup
  summarise(Count = n()) %>% 
  # Capitalizing "employed"
  mutate(Emp_status = str_replace(Emp_status, "^employed", "Employed")) %>% 
  # Capitalizing "unemployed"
  mutate(Emp_status = str_replace(Emp_status, "^unemployed", "Unemployed"))

# Generating plot
FeatRel02 <- plot_ly()
FeatRel02 <- FeatRel02 %>% 
  add_trace(
    data = subset(MarStat_EmpStat, Emp_status == "Employed"),
    x = ~Marital_status,
    y = ~Count,
    name = "Employed"
  ) %>% 
  add_trace(
    data = subset(MarStat_EmpStat, Emp_status == "Unemployed"),
    x = ~Marital_status,
    y = ~Count,
    name = "Unemployed"
  ) %>%
  add_trace(
    data = subset(MarStat_EmpStat, Emp_status == "Unknown"),
    x = ~Marital_status,
    y = ~Count,
    name = "Unknown"
  ) %>% 
  layout(
    title = "Marital Status and Employment Status",
    xaxis = list(title = "Marital Status"),
    yaxis = list(title = "Count"),
    legend = list(
      title = list(text = "<b> Employment Status </b>"),
      bgcolor = "#E2E2E2",
      bordercolor = "#FFFFFF",
      borderwidth = 2
    )
  )

# Outputting plot
FeatRel02

4.3 Marital_status and Saving_amount

Savings amounts do not appear to change according to marital status.

# ====================
# PLOTTING `Marital_status` AND `Saving_amount`
# ====================

# Preparing plot data
MarStat_SavAmou <- 
  # Subsetting loan_default_Mod01 to just Marital_status and Saving_amount
  loan_default_Mod01[ , c("Marital_status", "Saving_amount")] %>% 
  # Replacing missing values with the word "Unknown"
  replace_na(list(Marital_status = "Unknown"))

# Generating plot
FeatRel03 <- plot_ly() %>% 
  add_trace(
    data = MarStat_SavAmou,
    x = ~Saving_amount, 
    y = ~Marital_status, 
    type = "box",
    color = ~Marital_status
  ) %>% 
  layout(
    title = "Marital Status and Savings Amount",
    xaxis = list(title = "Savings Amount"),
    yaxis = list(
      title = "Marital Status", 
      showticklabels = FALSE, 
      categoryarray = list("Unknown", "Single", "Married"),
      categoryorder = "array"
    ),
    legend = list(
      title = list(text = "<b> Marital Status </b>"),
      bgcolor = "#E2E2E2",
      bordercolor = "#FFFFFF",
      borderwidth = 2
    )
  )

# Outputting plot
FeatRel03

4.4 Checking_amount, Credit_score, and Saving_amount

There do not appear to be more than one cluster of points or any other form of clear divide that would indicate additional categories for us to discover from this graph.

# ====================
# PLOTTING `Checking_amount`, `Credit_score`, AND `Saving_amount`
# ====================

# Preparing plot data
ChecAmou_CredSco_SavAmou <- 
  # Subsetting loan_default_Mod01 to just Checking_amount, Credit_score, and Saving_amount
  loan_default_Mod01[ , c("Checking_amount", "Credit_score", "Saving_amount")]

# Generating plot
FeatRel04 <- plot_ly() %>% 
  add_trace(
    data = ChecAmou_CredSco_SavAmou,
    x = ~Checking_amount, 
    y = ~Credit_score, 
    z = ~Saving_amount,
    marker = list(size = 2),
    hovertemplate = paste(
      "<b>Checking Amt</b>: %{x}<br>",
      "<b>Credit Score</b>: %{y}<br>",
      "<b>Saving Amt</b>: %{z}"
    ),
    name = ""
  ) %>%
  layout(
    title = "Checking Amount, Credit Score, and Savings Amount",
    scene = list(
      xaxis = list(title = "Checking Amount"),
      yaxis = list(title = "Credit Score"),
      zaxis = list(title = "Savings Amount"),
      aspectmode = "cube"
    )
  )

# Outputting plot
FeatRel04
LS0tDQp0aXRsZTogIkxvYW4gRGVmYXVsdCBEYXRhIFtJbnRha2UgYW5kIE92ZXJ2aWV3XSINCmF1dGhvcjogIktvamkgU2hpbW9tdXJhIg0KZGF0ZTogIiAiDQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6IA0KICAgIHRvYzogeWVzDQogICAgdG9jX2RlcHRoOiA0DQogICAgdG9jX2Zsb2F0OiB5ZXMNCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcw0KICAgIHRvY19jb2xsYXBzZWQ6IHllcw0KICAgIGNvZGVfZm9sZGluZzogaGlkZQ0KICAgIGNvZGVfZG93bmxvYWQ6IHllcw0KICAgIHNtb290aF9zY3JvbGw6IHllcw0KICAgIHRoZW1lOiBsdW1lbg0KICB3b3JkX2RvY3VtZW50OiANCiAgICB0b2M6IHllcw0KICAgIHRvY19kZXB0aDogNA0KICAgIGZpZ19jYXB0aW9uOiB5ZXMNCiAgICBrZWVwX21kOiB5ZXMNCiAgcGRmX2RvY3VtZW50OiANCiAgICB0b2M6IHllcw0KICAgIHRvY19kZXB0aDogNA0KICAgIGZpZ19jYXB0aW9uOiB5ZXMNCiAgICBudW1iZXJfc2VjdGlvbnM6IG5vDQogICAgZmlnX3dpZHRoOiAzDQogICAgZmlnX2hlaWdodDogMw0KZWRpdG9yX29wdGlvbnM6IA0KICBjaHVua19vdXRwdXRfdHlwZTogaW5saW5lDQotLS0NCg0KYGBgez1odG1sfQ0KDQo8c3R5bGUgdHlwZT0idGV4dC9jc3MiPg0KDQovKiBDYXNjYWRpbmcgU3R5bGUgU2hlZXRzIChDU1MpIGlzIGEgc3R5bGVzaGVldCBsYW5ndWFnZSB1c2VkIHRvIGRlc2NyaWJlIHRoZSBwcmVzZW50YXRpb24gb2YgYSBkb2N1bWVudCB3cml0dGVuIGluIEhUTUwgb3IgWE1MLiBpdCBpcyBhIHNpbXBsZSBtZWNoYW5pc20gZm9yIGFkZGluZyBzdHlsZSAoZS5nLiwgZm9udHMsIGNvbG9ycywgc3BhY2luZykgdG8gV2ViIGRvY3VtZW50cy4gKi8NCg0KaDEudGl0bGUgeyAgLyogVGl0bGUgLSBmb250IHNwZWNpZmljYXRpb25zIG9mIHRoZSByZXBvcnQgdGl0bGUgKi8NCiAgZm9udC1zaXplOiAyMnB4Ow0KICBmb250LXdlaWdodDogYm9sZDsNCiAgY29sb3I6IERhcmtSZWQ7DQogIHRleHQtYWxpZ246IGNlbnRlcjsNCiAgZm9udC1mYW1pbHk6ICJHaWxsIFNhbnMiLCBzYW5zLXNlcmlmOw0KfQ0KaDQuYXV0aG9yIHsgLyogSGVhZGVyIDQgLSBmb250IHNwZWNpZmljYXRpb25zIGZvciBhdXRob3JzICAqLw0KICBmb250LXNpemU6IDE4cHg7DQogIGZvbnQtd2VpZ2h0OiBib2xkOw0KICBmb250LWZhbWlseTogc3lzdGVtLXVpOw0KICBjb2xvcjogbmF2eTsNCiAgdGV4dC1hbGlnbjogY2VudGVyOw0KfQ0KaDQuZGF0ZSB7IC8qIEhlYWRlciA0IC0gZm9udCBzcGVjaWZpY2F0aW9ucyBmb3IgdGhlIGRhdGUgICovDQogIGZvbnQtc2l6ZTogMThweDsNCiAgZm9udC1mYW1pbHk6IHN5c3RlbS11aTsNCiAgY29sb3I6IERhcmtCbHVlOw0KICB0ZXh0LWFsaWduOiBjZW50ZXI7DQogIGZvbnQtd2VpZ2h0OiBib2xkOw0KfQ0KaDEgeyAvKiBIZWFkZXIgMSAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgZm9yIGxldmVsIDEgc2VjdGlvbiB0aXRsZSAgKi8NCiAgICBmb250LXNpemU6IDIycHg7DQogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7DQogICAgY29sb3I6IG5hdnk7DQogICAgdGV4dC1hbGlnbjogY2VudGVyOw0KICAgIGZvbnQtd2VpZ2h0OiBib2xkOw0KfQ0KaDIgeyAvKiBIZWFkZXIgMiAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgZm9yIGxldmVsIDIgc2VjdGlvbiB0aXRsZSAqLw0KICAgIGZvbnQtc2l6ZTogMjBweDsNCiAgICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsNCiAgICBjb2xvcjogbmF2eTsNCiAgICB0ZXh0LWFsaWduOiBsZWZ0Ow0KICAgIGZvbnQtd2VpZ2h0OiBib2xkOw0KfQ0KDQpoMyB7IC8qIEhlYWRlciAzIC0gZm9udCBzcGVjaWZpY2F0aW9ucyBvZiBsZXZlbCAzIHNlY3Rpb24gdGl0bGUgICovDQogICAgZm9udC1zaXplOiAxOHB4Ow0KICAgIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOw0KICAgIGNvbG9yOiBuYXZ5Ow0KICAgIHRleHQtYWxpZ246IGxlZnQ7DQp9DQoNCmg0IHsgLyogSGVhZGVyIDQgLSBmb250IHNwZWNpZmljYXRpb25zIG9mIGxldmVsIDQgc2VjdGlvbiB0aXRsZSAgKi8NCiAgICBmb250LXNpemU6IDE4cHg7DQogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7DQogICAgY29sb3I6IGRhcmtyZWQ7DQogICAgdGV4dC1hbGlnbjogbGVmdDsNCn0NCg0KYm9keSB7IGJhY2tncm91bmQtY29sb3I6d2hpdGU7IH0NCg0KLmhpZ2hsaWdodG1lIHsgYmFja2dyb3VuZC1jb2xvcjp5ZWxsb3c7IH0NCg0KcCB7IGJhY2tncm91bmQtY29sb3I6d2hpdGU7IH0NCg0KPC9zdHlsZT4NCmBgYA0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCiMgY29kZSBjaHVuayBzcGVjaWZpZXMgd2hldGhlciB0aGUgUiBjb2RlLCB3YXJuaW5ncywgYW5kIG91dHB1dCB3aWxsIGJlIGluY2x1ZGVkIGluIHRoZSBvdXRwdXQgZmlsZXMuDQppZiAoIXJlcXVpcmUoImtuaXRyIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoImtuaXRyIikNCiAgIGxpYnJhcnkoa25pdHIpDQp9DQppZiAoIXJlcXVpcmUoInRpZHl2ZXJzZSIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJ0aWR5dmVyc2UiKQ0KbGlicmFyeSh0aWR5dmVyc2UpDQp9DQppZiAoIXJlcXVpcmUoInBhbG1lcnBlbmd1aW5zIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoInBhbG1lcnBlbmd1aW5zIikNCmxpYnJhcnkocGFsbWVycGVuZ3VpbnMpDQp9DQppZiAoIXJlcXVpcmUoInBsb3RseSIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJwbG90bHkiKQ0KbGlicmFyeShwbG90bHkpDQp9DQppZiAoIXJlcXVpcmUoIkdHYWxseSIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJHR2FsbHkiKQ0KbGlicmFyeShHR2FsbHkpDQp9DQppZiAoIXJlcXVpcmUoIm5hbmlhciIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJuYW5pYXIiKQ0KbGlicmFyeShuYW5pYXIpDQp9DQppZiAoIXJlcXVpcmUoInBvb2wiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygicG9vbCIpDQpsaWJyYXJ5KHBvb2wpDQp9DQppZiAoIXJlcXVpcmUoIkRCSSIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJEQkkiKQ0KbGlicmFyeShEQkkpDQp9DQppZiAoIXJlcXVpcmUoIlJNeVNRTCIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJSTXlTUUwiKQ0KbGlicmFyeShSTXlTUUwpDQp9DQppZiAoIXJlcXVpcmUoInJhbmRvbUZvcmVzdCIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJyYW5kb21Gb3Jlc3QiKQ0KbGlicmFyeShyYW5kb21Gb3Jlc3QpDQp9DQppZiAoIXJlcXVpcmUoImdnaXJhcGgiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygiZ2dpcmFwaCIpDQpsaWJyYXJ5KGdnaXJhcGgpDQp9DQppZiAoIXJlcXVpcmUoImhpZ2hjaGFydGVyIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoImhpZ2hjaGFydGVyIikNCmxpYnJhcnkoaGlnaGNoYXJ0ZXIpDQp9DQppZiAoIXJlcXVpcmUoImJyb29tIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoImJyb29tIikNCmxpYnJhcnkoYnJvb20pDQp9DQojIyANCmtuaXRyOjpvcHRzX2NodW5rJHNldCgNCiAgIyBpbmNsdWRlIGNvZGUgY2h1bmsgaW4gdGhlIG91dHB1dCBmaWxlDQogIGVjaG8gPSBUUlVFLA0KICAjIHNvbWV0aW1lcywgeW91IGNvZGUgbWF5IHByb2R1Y2Ugd2FybmluZyBtZXNzYWdlcywgeW91IGNhbiBjaG9vc2UgdG8gaW5jbHVkZSB0aGUgd2FybmluZyBtZXNzYWdlcyBpbiB0aGUgb3V0cHV0IGZpbGUuDQogIHdhcm5pbmcgPSBGQUxTRSwgDQogICMgeW91IGNhbiBhbHNvIGRlY2lkZSB3aGV0aGVyIHRvIGluY2x1ZGUgdGhlIG91dHB1dCBpbiB0aGUgb3V0cHV0IGZpbGUuDQogIHJlc3VsdHMgPSBUUlVFLCANCiAgbWVzc2FnZSA9IEZBTFNFLA0KICBjb21tZW50ID0gTkENCikgIA0KYGBgDQoNCiMgSW50cm9kdWN0aW9uDQoNClRoZSBkYXRhIHV0aWxpemVkIGluIHRoaXMgYW5hbHlzaXMgaXMgZnJvbSBhIGRhdGFzZXQgY2FsbGVkIGBsb2FuX2RlZmF1bHRgIGZyb20gKkFwcGxpZWQgQW5hbHl0aWNzIHRocm91Z2ggQ2FzZSBTdHVkaWVzIFVzaW5nIFNBUyBhbmQgUiosIGJ5IERlZXB0aSBHdXB0YSwgcHVibGlzaGVkIGJ5IEFQcmVzcywgSVNCTiAtIDk3OC0xLTQ4NDItMzUyNS02Lg0KDQojIyBQdXJwb3NlIG9mIERhdGEgQ29sbGVjdGlvbg0KPCEtLSANClByb3ZpZGUgYSBjbGVhciBhbmQgY29uY2lzZSBleHBsYW5hdGlvbiBvZiB3aHkgdGhlIGRhdGEgaXMgYmVpbmcgY29sbGVjdGVkLCBoaWdobGlnaHRpbmcgdGhlIHNwZWNpZmljIG9iamVjdGl2ZXMgYW5kIGludGVuZGVkIHVzZSBvZiB0aGUgZGF0YS4gDQotLT4NCg0KVGhlIGRhdGEgZm9yIGBsb2FuX2RlZmF1bHRgIHdhcyBjb2xsZWN0ZWQgdG8gZ2VuZXJhdGUgYSBtb2RlbCBmb3IgcHJlZGljdGluZyB3aGljaCBjdXN0b21lcnMgYXJlIG1vc3QgbGlrZWx5IHRvIGRlZmF1bHQuDQoNCiMjIERlc2NyaXB0aW9uIG9mIHRoZSBEYXRhIENvbGxlY3Rpb24gb3IgR2VuZXJhdGlvbiBQcm9jZXNzDQo8IS0tIA0KT3V0bGluZSB0aGUgbWV0aG9kcyB1c2VkIHRvIGNvbGxlY3Qgb3IgZ2VuZXJhdGUgdGhlIGRhdGEsIGluY2x1ZGluZyBhbnkgdG9vbHMsIHRlY2hub2xvZ2llcywgb3IgcHJvdG9jb2xzIGZvbGxvd2VkLiBTcGVjaWZ5IHRoZSB0aW1lIGZyYW1lIGFuZCBsb2NhdGlvbiwgaWYgYXBwbGljYWJsZS4gDQotLT4NCg0KVGhlIGV4YWN0IG1ldGhvZHMgdXNlZCB0byBjb2xsZWN0IHRoZSBkYXRhIGluIGBsb2FuX2RlZmF1bHRgIGFyZSB1bmZvcnR1bmF0ZWx5IHVua25vd24uDQoNCiMjIFNhbXBsZSBTaXplIGFuZCBOdW1iZXIgb2YgRmVhdHVyZSBWYXJpYWJsZXMNCjwhLS0gDQpTdGF0ZSB0aGUgdG90YWwgc2FtcGxlIHNpemUgYW5kIHRoZSBudW1iZXIgb2YgZmVhdHVyZSB2YXJpYWJsZXMgaW5jbHVkZWQgaW4gdGhlIGRhdGEgc2V0LCBwcm92aWRpbmcgY29udGV4dCBmb3IgdGhlIHNjb3BlIGFuZCByZXByZXNlbnRhdGl2ZW5lc3Mgb2YgdGhlIGRhdGEuIA0KLS0+DQoNCmBsb2FuX2RlZmF1bHRgIGhhcyBhIHRvdGFsIHNhbXBsZSBzaXplIG9mIDEsMDAwLCBjb250YWlucyAxNSBmZWF0dXJlIHZhcmlhYmxlcywgYW5kIGhhcyAxIGxhYmVsLg0KDQojIyBJdGVtaXplZCBMaXN0IG9mIEZlYXR1cmUgVmFyaWFibGVzDQo8IS0tIA0KUHJlc2VudCBhIGRldGFpbGVkIGxpc3Qgb2YgZmVhdHVyZSB2YXJpYWJsZXMsIGluY2x1ZGluZzoNCiogRGVmaW5pdGlvbi9EZXNjcmlwdGlvbjogUHJvdmlkZSBhIGJyaWVmIGV4cGxhbmF0aW9uIG9mIHdoYXQgZWFjaCB2YXJpYWJsZSByZXByZXNlbnRzLg0KKiBEYXRhIFR5cGVzOiBTcGVjaWZ5IHRoZSB0eXBlIG9mIGRhdGEgKGUuZy4sIGNhdGVnb3JpY2FsLCBudW1lcmljYWwsIGJvb2xlYW4sIHRleHQpLiANCi0tPg0KDQpUaGUgZmVhdHVyZSB2YXJpYWJsZXMgb2YgYGxvYW5fZGVmYXVsdGAgYXJlIGFzIGZvbGxvd3M6DQoNCiogVmFyaWFibGUgMTogYENoZWNraW5nX0Ftb3VudGAgKE51bWVyaWMpDQoqIFZhcmlhYmxlIDI6IGBUZXJtYCAoZGlzcGxheWVkIGluIG1vbnRocyAoTnVtZXJpYykpDQoqIFZhcmlhYmxlIDM6IGBDcmVkaXRfc2NvcmVgIChOdW1lcmljKQ0KKiBWYXJpYWJsZSA0OiBgR2VuZGVyYCAoQ2F0ZWdvcmljYWwpDQoqIFZhcmlhYmxlIDU6IGBNYXJpdGFsX3N0YXR1c2AgKENhdGVnb3JpY2FsKQ0KKiBWYXJpYWJsZSA2OiBgQ2FyX2xvYW5gICgxLSBPd24gY2FyIGxvYW4sIDAtIERvZXMgbm90IG93biBjYXIgbG9hbiDigJMNCk51bWVyaWMpDQoqIFZhcmlhYmxlIDc6IGBQZXJzb25hbF9sb2FuYCAoMS0gT3duIFBlcnNvbmFsIGxvYW4sIDAtIERvZXMgbm90IG93bg0KUGVyc29uYWwgbG9hbiDigJMgTnVtZXJpYykNCiogVmFyaWFibGUgODogYEhvbWVfbG9hbmAgKDEtIE93biBIb21lIGxvYW4sIDAtIERvZXMgbm90IG93biBIb21lDQpsb2FuIOKAkyBOdW1lcmljKQ0KKiBWYXJpYWJsZSA5OiBgRWR1Y2F0aW9uX2xvYW5gICgxLSBPd24gRWR1Y2F0aW9uIGxvYW4sIDAtIERvZXMgbm90DQpvd24gRWR1Y2F0aW9uIGxvYW4g4oCTIE51bWVyaWMpDQoqIFZhcmlhYmxlIDEwOiBgRW1wX3N0YXR1c2AgKENhdGVnb3JpY2FsKQ0KKiBWYXJpYWJsZSAxMTogYEFtb3VudGAgKE51bWVyaWMpDQoqIFZhcmlhYmxlIDEyOiBgU2F2aW5nX2Ftb3VuYCAoTnVtZXJpYykNCiogVmFyaWFibGUgMTM6IGBFbXBfZHVyYXRpb25gICh3aGljaCBpcyBkaXNwbGF5ZWQgaW4gbW9udGhzDQooTnVtZXJpYykpDQoqIFZhcmlhYmxlIDE0OiBgQWdlYCAod2hpY2ggaXMgZGlzcGxheWVkIGluIHllYXJzIChOdW1lcmljKSkNCiogVmFyaWFibGUgMTU6IGBOb19vZl9jcmVkaXRfYWNjb3VudGAgKE51bWVyaWMpDQoNClRoZSBsYWJlbCB2YXJpYWJsZSBvZiBgbG9hbl9kZWZhdWx0YCBpczoNCg0KKiBgRGVmYXVsdGAgaXMgdGhlIHRhcmdldCBWYXJpYWJsZSBpbiBkYXRhc2V0IHdoZXJlIDEgZGlzcGxheXMgYmFuayBsb2FuDQpkZWZhdWx0IGFuZCAwIGRpc3BsYXlzIGJhbmsgbG9hbiBub24gZGVmYXVsdC4NCg0KVGhlIHNvdXJjZSBmb3IgdGhpcyBpbmZvcm1hdGlvbiBjYW4gYmUgZm91bmQgaW4gYSBQREYgY2FsbGVkIFtCYW5rTG9hbkRlZmF1bHREYXRhc2V0LURlc2NyaXB0aW9uLnBkZl0oaHR0cHM6Ly9naXRodWIuY29tL3NhbHR3YXRlcnNvdXAvTXlDVi9ibG9iLzhjNWQwODE3OWU3MTIwMTI2NTM3ZjZkYjA3Y2MwNGU1YTc0MTEzN2UvRGF0YSUyMFNldHMvTG9hbiUyMERlZmF1bHQlMjBEYXRhL0JhbmtMb2FuRGVmYXVsdERhdGFzZXQtRGVzY3JpcHRpb24ucGRmKQ0KDQojIFdvcmtpbmcgRGF0YXNldA0KDQpGb3IgdGhlIHB1cnBvc2VzIG9mIGRlbW9uc3RyYXRpbmcgaW1wdXRhdGlvbiBpbiB0aGUgZnV0dXJlLCBtaXNzaW5nIHZhbHVlcyB3ZXJlIGdlbmVyYXRlZCBmb3IgZWFjaCBvZiBgR2VuZGVyYCwgYE1hcml0YWxfc3RhdHVzYCwgYEVtcF9zdGF0dXNgLCBgRW1wX2R1cmF0aW9uYCwgYW5kIGBBZ2VgIHRvIGdlbmVyYXRlIHRoZSB3b3JraW5nIGRhdGFzZXQgYGxvYW5fZGVmYXVsdF9Nb2QwMWAuDQoNCmBgYHtyfQ0KIyA9PT09PT09PT09PT09PT09PT09PQ0KIyBEQVRBIElOVEFLRQ0KIyA9PT09PT09PT09PT09PT09PT09PQ0KDQpsb2FuX2RlZmF1bHQgPC0gcmVhZC5jc3YoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9zYWx0d2F0ZXJzb3VwL015Q1YvcmVmcy9oZWFkcy9tYWluL0RhdGElMjBTZXRzL0xvYW4lMjBEZWZhdWx0JTIwRGF0YS9CYW5rTG9hbkRlZmF1bHREYXRhc2V0LmNzdiIpDQpgYGANCg0KYGBge3J9DQojID09PT09PT09PT09PT09PT09PT09DQojIERBVEEgTU9ESUZJQ0FUSU9ODQojIEFkZGluZyBtaXNzaW5nIHZhbHVlcw0KIyA9PT09PT09PT09PT09PT09PT09PQ0KDQojIENvcHlpbmcgbG9hbl9kZWZhdWx0DQpsb2FuX2RlZmF1bHRfTW9kMDEgPC0gbG9hbl9kZWZhdWx0DQoNCiMgQ3JlYXRpbmcgcmFuZG9tIG9ic2VydmF0aW9uIElEcyBhbmQgcmVwbGFjaW5nIHRoZSBjb3JyZXNwb25kaW5nIG9ic2VydmF0aW9ucyB3aXRoIG1pc3NpbmcNCg0KIyBsb2FuX2RlZmF1bHRfTW9kMDEkQ2hlY2tpbmdfQW1vdW50W3NhbXBsZSgxOjEwMDAsIDEwMCwgcmVwbGFjZSA9IEZBTFNFKV0gPC0gTkENCiMgbG9hbl9kZWZhdWx0X01vZDAxJFRlcm1bc2FtcGxlKDE6MTAwMCwgMTAwLCByZXBsYWNlID0gRkFMU0UpXSA8LSBOQQ0KIyBsb2FuX2RlZmF1bHRfTW9kMDEkQ3JlZGl0X3Njb3JlW3NhbXBsZSgxOjEwMDAsIDEwMCwgcmVwbGFjZSA9IEZBTFNFKV0gPC0gTkENCmxvYW5fZGVmYXVsdF9Nb2QwMSRHZW5kZXJbc2FtcGxlKDE6MTAwMCwgNjgsIHJlcGxhY2UgPSBGQUxTRSldIDwtIE5BDQpsb2FuX2RlZmF1bHRfTW9kMDEkTWFyaXRhbF9zdGF0dXNbc2FtcGxlKDE6MTAwMCwgODcsIHJlcGxhY2UgPSBGQUxTRSldIDwtIE5BDQojIGxvYW5fZGVmYXVsdF9Nb2QwMSRDYXJfbG9hbltzYW1wbGUoMToxMDAwLCAxMDAsIHJlcGxhY2UgPSBGQUxTRSldIDwtIE5BDQojIGxvYW5fZGVmYXVsdF9Nb2QwMSRQZXJzb25hbF9sb2FuW3NhbXBsZSgxOjEwMDAsIDEwMCwgcmVwbGFjZSA9IEZBTFNFKV0gPC0gTkENCiMgbG9hbl9kZWZhdWx0X01vZDAxJEhvbWVfbG9hbltzYW1wbGUoMToxMDAwLCAxMDAsIHJlcGxhY2UgPSBGQUxTRSldIDwtIE5BDQojIGxvYW5fZGVmYXVsdF9Nb2QwMSRFZHVjYXRpb25fbG9hbltzYW1wbGUoMToxMDAwLCAxMDAsIHJlcGxhY2UgPSBGQUxTRSldIDwtIE5BDQpsb2FuX2RlZmF1bHRfTW9kMDEkRW1wX3N0YXR1c1tzYW1wbGUoMToxMDAwLCAxMzYsIHJlcGxhY2UgPSBGQUxTRSldIDwtIE5BDQojIGxvYW5fZGVmYXVsdF9Nb2QwMSRBbW91bnRbc2FtcGxlKDE6MTAwMCwgMTAwLCByZXBsYWNlID0gRkFMU0UpXSA8LSBOQQ0KIyBsb2FuX2RlZmF1bHRfTW9kMDEkU2F2aW5nX2Ftb3VuW3NhbXBsZSgxOjEwMDAsIDEwMCwgcmVwbGFjZSA9IEZBTFNFKV0gPC0gTkENCmxvYW5fZGVmYXVsdF9Nb2QwMSRFbXBfZHVyYXRpb25bc2FtcGxlKDE6MTAwMCwgMjAxLCByZXBsYWNlID0gRkFMU0UpXSA8LSBOQQ0KbG9hbl9kZWZhdWx0X01vZDAxJEFnZVtzYW1wbGUoMToxMDAwLCAxNTksIHJlcGxhY2UgPSBGQUxTRSldIDwtIE5BDQojIGxvYW5fZGVmYXVsdF9Nb2QwMSROb19vZl9jcmVkaXRfYWNjb3VudFtzYW1wbGUoMToxMDAwLCAxMDAsIHJlcGxhY2UgPSBGQUxTRSldIDwtIE5BDQpgYGANCg0KIyBEaXN0cmlidXRpb24gb2YgSW5kaXZpZHVhbCBGZWF0dXJlcw0KDQpQcmVwYXJhdGlvbiBmb3IgYW5hbHlzaXMgY2FsbHMgZm9yIHRoZSBmb2xsb3dpbmcgYW5hbHl0aWMgdGFza3M6DQoNCjEuIEhhbmRsaW5nIE1pc3NpbmcgVmFsdWVzDQoyLiBBZGRyZXNzaW5nIE91dGxpZXJzDQoNCiMjIEhhbmRsaW5nIE1pc3NpbmcgVmFsdWVzDQoNCmBsb2FuX2RlZmF1bHRfTW9kMDFgIGhhcyBtaXNzaW5nIHZhbHVlcywgaW4gdGhlIHZhcmlhYmxlcyBgR2VuZGVyYCwgYE1hcml0YWxfc3RhdHVzYCwgYEVtcF9zdGF0dXNgLCBgRW1wX2R1cmF0aW9uYCwgYW5kIGBBZ2VgLiBUbyB1dGlsaXplIG9ic2VydmF0aW9ucyB3aXRoIG1pc3NpbmcgZGF0YSwgaW1wdXRhdGlvbiB3aWxsIGJlIHBlcmZvcm1lZCBwcmlvciB0byBhbmFseXNpcy4NCg0KYGBge3J9DQojID09PT09PT09PT09PT09PT09PT09DQojIFBMT1RUSU5HIE1JU1NJTkcgVkFMVUVTDQojID09PT09PT09PT09PT09PT09PT09DQoNCiMgR2VuZXJhdGluZyBkYXRhIGZyYW1lIG9mIG1pc3NpbmcgdmFsdWVzIHBlciB2YXJpYWJsZQ0KTWlzc0RhdENvdW50cyA8LSBkYXRhLmZyYW1lKA0KICBWYXJpYWJsZXMgPSBuYW1lcyhsb2FuX2RlZmF1bHRfTW9kMDEpLA0KICBNaXNzaW5nID0gY29sU3Vtcyhpcy5uYShsb2FuX2RlZmF1bHRfTW9kMDEpKQ0KKQ0KDQojIEdlbmVyYXRpbmcgaW50ZXJhY3RpdmUgcGxvdCB1c2luZyBwbG90bHkNClBsb3RfTWlzc2luZ1ZhbHMgPC0gDQogICMgVGFraW5nIGEgc3Vic2V0IG9mIE1pc3NEYXRDb3VudHMsIHNvIG9ubHkgZW50cmllcyB3aXRoID4gMCBtaXNzaW5nIHZhbHVlcyB3aWxsIGJlIGRpc3BsYXllZA0KICBzdWJzZXQoTWlzc0RhdENvdW50cywgTWlzc2luZyA+IDApICU+JSANCiAgIyBQYXNzaW5nIHRoZSBzdWJzZXQgdG8gcGxvdF9seQ0KICBwbG90X2x5KA0KICAgIHggPSB+VmFyaWFibGVzLA0KICAgIHkgPSB+TWlzc2luZw0KICApICU+JSANCiAgbGF5b3V0KA0KICAgIHRpdGxlID0gbGlzdCgNCiAgICAgIHRleHQgPSAiTWlzc2luZyBWYWx1ZXMgcGVyIFZhcmlhYmxlIg0KICAgICksDQogICAgeGF4aXMgPSBsaXN0KA0KICAgICAgdGl0bGUgPSAiVmFyaWFibGVzIHdpdGggTWlzc2luZyBWYWx1ZXMiLA0KICAgICAgY2F0ZWdvcnlvcmRlciA9ICJ0cmFjZSINCiAgICApLA0KICAgIHlheGlzID0gbGlzdCgNCiAgICAgIHRpdGxlID0gIk51bWJlciBvZiBNaXNzaW5nIFZhbHVlcyINCiAgICApDQogICkNCg0KIyBPdXRwdXR0aW5nIHBsb3QNClBsb3RfTWlzc2luZ1ZhbHMNCmBgYA0KDQojIyBBZGRyZXNzaW5nIE91dGxpZXJzDQoNCk9mIHBhcnRpY3VsYXIgbm90ZSBpcyB0aGUgdXBwZXIgZW5kIG9mIGNyZWRpdCBzY29yZXMuIFR5cGljYWwgY3JlZGl0IHNjb3JlcyAoRklDTyBjcmVkaXQgc2NvcmVzKSByYW5nZSBmcm9tIDMwMCB0byA4NTAuIEhvd2V2ZXIsIHRoZXJlIGFyZSBzb21lIGxlc3MgY29tbW9uIHNjb3JpbmcgbW9kZWxzIHRoYXQgdXRpbGl6ZSBkaWZmZXJlbnQgbnVtZXJpY2FsIHJhbmdlcy4gVGhlIEZJQ08gTmV4dEdlbiByYW5nZXMgZnJvbSAxNTAgdG8gOTUwLiBUaG91Z2ggZGlzY29udGludWVkLCBWYW50YWdlU2NvcmUgMS4wIGFuZCAyLjAgcmFuZ2VkIGZyb20gNTAxIHRvIDk5MC4NCg0KVGhlcmUgYXJlIHNvbWUgYENyZWRpdF9zY29yZWAgdmFsdWVzIHRoYXQgZXhjZWVkIDk5MCwgYnV0IGl0IGlzIHBvc3NpYmxlIHRoYXQgdGhvc2UgdmFsdWVzIG1heSBiZSB0aGUgcmVzdWx0IG9mIHNvbWUgcmFyZXIgc2NvcmluZyBtb2RlbHMuIElmIHN1Y2ggYSBzY29yaW5nIG1vZGVsIGNhbm5vdCBiZSBpZGVudGlmaWVkLCB0aGVuIGl0IG1heSBiZSB3b3J0aHdoaWxlIHRvIGNvbnZlcnQgY3JlZGl0IHNjb3JlIHZhbHVlcyBvdmVyIDk5MCB0byBtaXNzaW5nIHZhbHVlcy4NCg0KYGBge3J9DQojID09PT09PT09PT09PT09PT09PT09DQojIFBMT1RUSU5HIEFMTCBOVU1FUklDQUwgTk9OLUJJTkFSWSBWQVJJQUJMRVMNCiMgPT09PT09PT09PT09PT09PT09PT0NCg0KIyBTZWxlY3Rpbmcgb25seSBudW1lcmljIHZhcmlhYmxlcw0KTnVtVmFycyA8LSBzZWxlY3QobG9hbl9kZWZhdWx0X01vZDAxLCB3aGVyZShpcy5udW1lcmljKSkNCiMgU2VsZWN0aW5nIGVsaW1pbmF0aW5nIGFueSBiaW5hcnkgdmFyaWFibGVzDQpOdW1WYXJzIDwtIE51bVZhcnNbIWFwcGx5KE51bVZhcnMsIDIsIGZ1bmN0aW9uKHgpe2FsbChtYXRjaCh4LCBjKDAsIDEsIE5BKSwgbm9tYXRjaCA9IEZBTFNFKSl9KV0NCg0KIyBQcmVwYXJpbmcgYSBsaXN0IG9mIHN1YnBsb3RzDQpOdW1GaWcgPC0gYygpDQojIFVzaW5nIGEgZm9yIGxvb3AgdG8gZ2VuZXJhdGUgYSBzdWJwbG90IHBlciB2YXJpYWJsZSBpbiBOdW1WYXJzDQpmb3IoaSBpbiAxOmxlbmd0aChuYW1lcyhOdW1WYXJzKSkpew0KICBOdW1GaWdbW2ldXSA8LSBwbG90X2x5KA0KICAgIHggPSBOdW1WYXJzW1tpXV0sIA0KICAgIHkgPSAiIiwgDQogICAgdHlwZSA9ICJib3giLA0KICAgIG5hbWUgPSBjb2xuYW1lcyhOdW1WYXJzKVtpXQ0KICApDQp9DQoNCiMgR2VuZXJhdGluZyBhIHBsb3QgdGhhdCBjb250YWlucyA4IHN1YnBsb3RzIChvbmUgZm9yIGVhY2ggdmFyaWFibGUgaW4gTnVtVmFycykgYWNyb3NzIDQgcm93cw0KUGxvdF9OdW1WYXJzIDwtIA0KICBzdWJwbG90KE51bUZpZ1tbMV1dLCBOdW1GaWdbWzJdXSwgTnVtRmlnW1szXV0sIE51bUZpZ1tbNF1dLCBOdW1GaWdbWzVdXSwgTnVtRmlnW1s2XV0sIE51bUZpZ1tbN11dLCBOdW1GaWdbWzhdXSwgbnJvd3MgPSA0LCBtYXJnaW4gPSAwLjA1KSAlPiUgDQogIGxheW91dCgNCiAgICB0aXRsZSA9ICJEaXN0cmlidXRpb25zIG9mIEFsbCBOdW1lcmljYWwgTm9uLWJpbmFyeSBWYXJpYWJsZXMiLA0KICAgIGxlZ2VuZCA9IGxpc3QoDQogICAgICB0aXRsZSA9IGxpc3QodGV4dCA9ICI8Yj4gVmFyaWFibGUgPC9iPiIpLA0KICAgICAgYmdjb2xvciA9ICIjRTJFMkUyIiwNCiAgICAgIGJvcmRlcmNvbG9yID0gIiNGRkZGRkYiLA0KICAgICAgYm9yZGVyd2lkdGggPSAyDQogICAgKQ0KICApDQoNCiMgT3V0cHV0dGluZyBwbG90DQpQbG90X051bVZhcnMNCmBgYA0KDQpgYGB7ciBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQ0KIyA9PT09PT09PT09PT09PT09PT09PQ0KIyBQTE9UVElORyBBTEwgTlVNRVJJQ0FMIE5PTi1CSU5BUlkgVkFSSUFCTEVTIE9OIFNBTUUgQVhJUw0KIyBUaGlzIGlzIG15IGluaXRpYWwgdmVyc2lvbiBvZiB0aGUgcGxvdCwgd2hpY2ggaXNuJ3QgcGFydGljdWxhcmx5IHVzZWZ1bCBiZWNhdXNlIHRoZSByYW5nZXMgZm9yIGVhY2ggdmFyaWFibGUgYXJlIHNvIGRpZmZlcmVudC4NCiMgPT09PT09PT09PT09PT09PT09PT0NCg0KIyBTZWxlY3Rpbmcgb25seSBudW1lcmljIHZhcmlhYmxlcw0KTnVtVmFycyA8LSBzZWxlY3QobG9hbl9kZWZhdWx0X01vZDAxLCB3aGVyZShpcy5udW1lcmljKSkNCiMgU2VsZWN0aW5nIGVsaW1pbmF0aW5nIGFueSBiaW5hcnkgdmFyaWFibGVzDQpOdW1WYXJzIDwtIE51bVZhcnNbIWFwcGx5KE51bVZhcnMsIDIsIGZ1bmN0aW9uKHgpe2FsbChtYXRjaCh4LCBjKDAsIDEsIE5BKSwgbm9tYXRjaCA9IEZBTFNFKSl9KV0NCg0KIyBQcmVwYXJpbmcgYSBwbG90DQpQbG90X051bVZhcnMgPC0gcGxvdF9seSgpDQojIFVzaW5nIGEgZm9yIGxvb3AgdG8gYWRkIGVhY2ggTnVtVmFycyB2YXJpYWJsZSB0byB0aGUgcGxvdA0KZm9yKGkgaW4gMTpsZW5ndGgobmFtZXMoTnVtVmFycykpKXsNCiAgUGxvdF9OdW1WYXJzIDwtIGFkZF90cmFjZSgNCiAgICBkYXRhID0gUGxvdF9OdW1WYXJzLA0KICAgIHggPSBOdW1WYXJzW1tpXV0sIA0KICAgIHR5cGUgPSAiYm94IiwgDQogICAgbmFtZSA9IGNvbG5hbWVzKE51bVZhcnMpW2ldDQogICkNCn0NCg0KIyBPdXRwdXR0aW5nIHBsb3QNClBsb3RfTnVtVmFycw0KYGBgDQoNCiMgUmVsYXRpb25zaGlwIEJldHdlZW4gRmVhdHVyZXMNCg0KVGhpcyBzZWN0aW9uIGV4cGxvcmVzIHRoZSBwb3RlbnRpYWwgcmVsYXRpb25zaGlwcyBiZXR3ZWVuIGZlYXR1cmUgdmFyaWFibGVzLCBzcGVjaWZpY2FsbHk6DQoNCjEuIGBDaGVja2luZ19hbW91bnRgIGFuZCBgQ3JlZGl0X3Njb3JlYA0KMi4gYE1hcml0YWxfc3RhdHVzYCBhbmQgYEVtcF9zdGF0dXNgDQozLiBgTWFyaXRhbF9zdGF0dXNgIGFuZCBgU2F2aW5nX2Ftb3VudGANCjQuIGBDaGVja2luZ19hbW91bnRgLCBgQ3JlZGl0X3Njb3JlYCwgYW5kIGBTYXZpbmdfYW1vdW50YA0KDQojIyBgQ2hlY2tpbmdfYW1vdW50YCBhbmQgYENyZWRpdF9zY29yZWANCg0KSXQgYXBwZWFycyB0aGF0IGhpZ2hlciBjaGVja2luZyBhbW91bnRzIGFyZSBhc3NvY2lhdGVkIHdpdGggYSBuYXJyb3dlciByYW5nZSBvZiBjcmVkaXQgc2NvcmVzLiBUaGVyZWZvcmUsIHRoZXJlIG1heSBiZSBhbiBhc3NvY2lhdGlvbiBiZXR3ZWVuIGNoZWNraW5nIGFtb3VudCBhbmQgY3JlZGl0IHNjb3JlIHRoYXQgaXMgd29ydGggbG9va2luZyBpbnRvLg0KDQpgYGB7cn0NCiMgPT09PT09PT09PT09PT09PT09PT0NCiMgUExPVFRJTkcgYENoZWNraW5nX2Ftb3VudGAgQU5EIGBDcmVkaXRfc2NvcmVgDQojID09PT09PT09PT09PT09PT09PT09DQoNCiMgR2VuZXJhdGluZyBwbG90DQpGZWF0UmVsMDEgPC0gDQogIHBsb3RfbHkoDQogICAgZGF0YSA9IGxvYW5fZGVmYXVsdF9Nb2QwMSwNCiAgICB4ID0gfkNoZWNraW5nX2Ftb3VudCwNCiAgICB5ID0gfkNyZWRpdF9zY29yZQ0KICApICU+JSANCiAgbGF5b3V0KA0KICAgIHRpdGxlID0gIkNoZWNraW5nIEFtb3VudCBhbmQgQ3JlZGl0IFNjb3JlIiwNCiAgICB4YXhpcyA9IGxpc3QodGl0bGUgPSAiQ2hlY2tpbmcgQW1vdW50IiksDQogICAgeWF4aXMgPSBsaXN0KHRpdGxlID0gIkNyZWRpdCBTY29yZSIpDQogICkNCg0KIyBPdXRwdXR0aW5nIHBsb3QNCkZlYXRSZWwwMQ0KYGBgDQoNCiMjIGBNYXJpdGFsX3N0YXR1c2AgYW5kIGBFbXBfc3RhdHVzYA0KDQpGb3IgYWxsIG1hcml0YWwgc3RhdHVzZXMsIHVuZW1wbG95ZWQgb3V0bnVtYmVycyBib3RoIGVtcGxveWVkIGFuZCB1bmtub3duLiBIb3dldmVyLCBtYXJyaWVkLXVuZW1wbG95ZWQgaW5kaXZpZHVhbHMgb3V0bnVtYmVyIGV2ZXJ5IG90aGVyIGNhdGVnb3J5LiBBc3NvY2lhdGlvbnMgbGlrZSB0aGlzIG1heSBwcm92ZSB1c2VmdWwgZm9yIGltcHV0YXRpb24gb2YgdW5rbm93bnMuDQoNCmBgYHtyfQ0KIyA9PT09PT09PT09PT09PT09PT09PQ0KIyBQTE9UVElORyBgTWFyaXRhbF9zdGF0dXNgIEFORCBgRW1wX3N0YXR1c2ANCiMgPT09PT09PT09PT09PT09PT09PT0NCg0KIyBQcmVwYXJpbmcgcGxvdCBkYXRhDQpNYXJTdGF0X0VtcFN0YXQgPC0gDQogICMgU3Vic2V0dGluZyBsb2FuX2RlZmF1bHRfTW9kMDEgdG8ganVzdCBNYXJpdGFsX3N0YXR1cyBhbmQgU2F2aW5nX2Ftb3VudA0KICBsb2FuX2RlZmF1bHRfTW9kMDFbICwgYygiTWFyaXRhbF9zdGF0dXMiLCAiRW1wX3N0YXR1cyIpXSAlPiUgDQogICMgUmVwbGFjaW5nIG1pc3NpbmcgdmFsdWVzIHdpdGggdGhlIHdvcmQgIlVua25vd24iDQogIHJlcGxhY2VfbmEobGlzdChNYXJpdGFsX3N0YXR1cyA9ICJVbmtub3duIiwgRW1wX3N0YXR1cyA9ICJVbmtub3duIikpICU+JQ0KICAjIEdyb3VwaW5nIGRhdGEgYnkgTWFyaXRhbF9zdGF0dXMgYW5kIEVtcF9zdGF0dXMNCiAgZ3JvdXBfYnkoTWFyaXRhbF9zdGF0dXMsIEVtcF9zdGF0dXMpICU+JSANCiAgIyBDb3VudGluZyB0aGUgbnVtYmVyIG9mIGVudHJpZXMgaW4gZWFjaCBzdWJncm91cA0KICBzdW1tYXJpc2UoQ291bnQgPSBuKCkpICU+JSANCiAgIyBDYXBpdGFsaXppbmcgImVtcGxveWVkIg0KICBtdXRhdGUoRW1wX3N0YXR1cyA9IHN0cl9yZXBsYWNlKEVtcF9zdGF0dXMsICJeZW1wbG95ZWQiLCAiRW1wbG95ZWQiKSkgJT4lIA0KICAjIENhcGl0YWxpemluZyAidW5lbXBsb3llZCINCiAgbXV0YXRlKEVtcF9zdGF0dXMgPSBzdHJfcmVwbGFjZShFbXBfc3RhdHVzLCAiXnVuZW1wbG95ZWQiLCAiVW5lbXBsb3llZCIpKQ0KDQojIEdlbmVyYXRpbmcgcGxvdA0KRmVhdFJlbDAyIDwtIHBsb3RfbHkoKQ0KRmVhdFJlbDAyIDwtIEZlYXRSZWwwMiAlPiUgDQogIGFkZF90cmFjZSgNCiAgICBkYXRhID0gc3Vic2V0KE1hclN0YXRfRW1wU3RhdCwgRW1wX3N0YXR1cyA9PSAiRW1wbG95ZWQiKSwNCiAgICB4ID0gfk1hcml0YWxfc3RhdHVzLA0KICAgIHkgPSB+Q291bnQsDQogICAgbmFtZSA9ICJFbXBsb3llZCINCiAgKSAlPiUgDQogIGFkZF90cmFjZSgNCiAgICBkYXRhID0gc3Vic2V0KE1hclN0YXRfRW1wU3RhdCwgRW1wX3N0YXR1cyA9PSAiVW5lbXBsb3llZCIpLA0KICAgIHggPSB+TWFyaXRhbF9zdGF0dXMsDQogICAgeSA9IH5Db3VudCwNCiAgICBuYW1lID0gIlVuZW1wbG95ZWQiDQogICkgJT4lDQogIGFkZF90cmFjZSgNCiAgICBkYXRhID0gc3Vic2V0KE1hclN0YXRfRW1wU3RhdCwgRW1wX3N0YXR1cyA9PSAiVW5rbm93biIpLA0KICAgIHggPSB+TWFyaXRhbF9zdGF0dXMsDQogICAgeSA9IH5Db3VudCwNCiAgICBuYW1lID0gIlVua25vd24iDQogICkgJT4lIA0KICBsYXlvdXQoDQogICAgdGl0bGUgPSAiTWFyaXRhbCBTdGF0dXMgYW5kIEVtcGxveW1lbnQgU3RhdHVzIiwNCiAgICB4YXhpcyA9IGxpc3QodGl0bGUgPSAiTWFyaXRhbCBTdGF0dXMiKSwNCiAgICB5YXhpcyA9IGxpc3QodGl0bGUgPSAiQ291bnQiKSwNCiAgICBsZWdlbmQgPSBsaXN0KA0KICAgICAgdGl0bGUgPSBsaXN0KHRleHQgPSAiPGI+IEVtcGxveW1lbnQgU3RhdHVzIDwvYj4iKSwNCiAgICAgIGJnY29sb3IgPSAiI0UyRTJFMiIsDQogICAgICBib3JkZXJjb2xvciA9ICIjRkZGRkZGIiwNCiAgICAgIGJvcmRlcndpZHRoID0gMg0KICAgICkNCiAgKQ0KDQojIE91dHB1dHRpbmcgcGxvdA0KRmVhdFJlbDAyDQpgYGANCg0KIyMgYE1hcml0YWxfc3RhdHVzYCBhbmQgYFNhdmluZ19hbW91bnRgDQoNClNhdmluZ3MgYW1vdW50cyBkbyBub3QgYXBwZWFyIHRvIGNoYW5nZSBhY2NvcmRpbmcgdG8gbWFyaXRhbCBzdGF0dXMuDQoNCmBgYHtyfQ0KIyA9PT09PT09PT09PT09PT09PT09PQ0KIyBQTE9UVElORyBgTWFyaXRhbF9zdGF0dXNgIEFORCBgU2F2aW5nX2Ftb3VudGANCiMgPT09PT09PT09PT09PT09PT09PT0NCg0KIyBQcmVwYXJpbmcgcGxvdCBkYXRhDQpNYXJTdGF0X1NhdkFtb3UgPC0gDQogICMgU3Vic2V0dGluZyBsb2FuX2RlZmF1bHRfTW9kMDEgdG8ganVzdCBNYXJpdGFsX3N0YXR1cyBhbmQgU2F2aW5nX2Ftb3VudA0KICBsb2FuX2RlZmF1bHRfTW9kMDFbICwgYygiTWFyaXRhbF9zdGF0dXMiLCAiU2F2aW5nX2Ftb3VudCIpXSAlPiUgDQogICMgUmVwbGFjaW5nIG1pc3NpbmcgdmFsdWVzIHdpdGggdGhlIHdvcmQgIlVua25vd24iDQogIHJlcGxhY2VfbmEobGlzdChNYXJpdGFsX3N0YXR1cyA9ICJVbmtub3duIikpDQoNCiMgR2VuZXJhdGluZyBwbG90DQpGZWF0UmVsMDMgPC0gcGxvdF9seSgpICU+JSANCiAgYWRkX3RyYWNlKA0KICAgIGRhdGEgPSBNYXJTdGF0X1NhdkFtb3UsDQogICAgeCA9IH5TYXZpbmdfYW1vdW50LCANCiAgICB5ID0gfk1hcml0YWxfc3RhdHVzLCANCiAgICB0eXBlID0gImJveCIsDQogICAgY29sb3IgPSB+TWFyaXRhbF9zdGF0dXMNCiAgKSAlPiUgDQogIGxheW91dCgNCiAgICB0aXRsZSA9ICJNYXJpdGFsIFN0YXR1cyBhbmQgU2F2aW5ncyBBbW91bnQiLA0KICAgIHhheGlzID0gbGlzdCh0aXRsZSA9ICJTYXZpbmdzIEFtb3VudCIpLA0KICAgIHlheGlzID0gbGlzdCgNCiAgICAgIHRpdGxlID0gIk1hcml0YWwgU3RhdHVzIiwgDQogICAgICBzaG93dGlja2xhYmVscyA9IEZBTFNFLCANCiAgICAgIGNhdGVnb3J5YXJyYXkgPSBsaXN0KCJVbmtub3duIiwgIlNpbmdsZSIsICJNYXJyaWVkIiksDQogICAgICBjYXRlZ29yeW9yZGVyID0gImFycmF5Ig0KICAgICksDQogICAgbGVnZW5kID0gbGlzdCgNCiAgICAgIHRpdGxlID0gbGlzdCh0ZXh0ID0gIjxiPiBNYXJpdGFsIFN0YXR1cyA8L2I+IiksDQogICAgICBiZ2NvbG9yID0gIiNFMkUyRTIiLA0KICAgICAgYm9yZGVyY29sb3IgPSAiI0ZGRkZGRiIsDQogICAgICBib3JkZXJ3aWR0aCA9IDINCiAgICApDQogICkNCg0KIyBPdXRwdXR0aW5nIHBsb3QNCkZlYXRSZWwwMw0KYGBgDQoNCg0KIyMgYENoZWNraW5nX2Ftb3VudGAsIGBDcmVkaXRfc2NvcmVgLCBhbmQgYFNhdmluZ19hbW91bnRgDQoNClRoZXJlIGRvIG5vdCBhcHBlYXIgdG8gYmUgbW9yZSB0aGFuIG9uZSBjbHVzdGVyIG9mIHBvaW50cyBvciBhbnkgb3RoZXIgZm9ybSBvZiBjbGVhciBkaXZpZGUgdGhhdCB3b3VsZCBpbmRpY2F0ZSBhZGRpdGlvbmFsIGNhdGVnb3JpZXMgZm9yIHVzIHRvIGRpc2NvdmVyIGZyb20gdGhpcyBncmFwaC4NCg0KYGBge3J9DQojID09PT09PT09PT09PT09PT09PT09DQojIFBMT1RUSU5HIGBDaGVja2luZ19hbW91bnRgLCBgQ3JlZGl0X3Njb3JlYCwgQU5EIGBTYXZpbmdfYW1vdW50YA0KIyA9PT09PT09PT09PT09PT09PT09PQ0KDQojIFByZXBhcmluZyBwbG90IGRhdGENCkNoZWNBbW91X0NyZWRTY29fU2F2QW1vdSA8LSANCiAgIyBTdWJzZXR0aW5nIGxvYW5fZGVmYXVsdF9Nb2QwMSB0byBqdXN0IENoZWNraW5nX2Ftb3VudCwgQ3JlZGl0X3Njb3JlLCBhbmQgU2F2aW5nX2Ftb3VudA0KICBsb2FuX2RlZmF1bHRfTW9kMDFbICwgYygiQ2hlY2tpbmdfYW1vdW50IiwgIkNyZWRpdF9zY29yZSIsICJTYXZpbmdfYW1vdW50IildDQoNCiMgR2VuZXJhdGluZyBwbG90DQpGZWF0UmVsMDQgPC0gcGxvdF9seSgpICU+JSANCiAgYWRkX3RyYWNlKA0KICAgIGRhdGEgPSBDaGVjQW1vdV9DcmVkU2NvX1NhdkFtb3UsDQogICAgeCA9IH5DaGVja2luZ19hbW91bnQsIA0KICAgIHkgPSB+Q3JlZGl0X3Njb3JlLCANCiAgICB6ID0gflNhdmluZ19hbW91bnQsDQogICAgbWFya2VyID0gbGlzdChzaXplID0gMiksDQogICAgaG92ZXJ0ZW1wbGF0ZSA9IHBhc3RlKA0KICAgICAgIjxiPkNoZWNraW5nIEFtdDwvYj46ICV7eH08YnI+IiwNCiAgICAgICI8Yj5DcmVkaXQgU2NvcmU8L2I+OiAle3l9PGJyPiIsDQogICAgICAiPGI+U2F2aW5nIEFtdDwvYj46ICV7en0iDQogICAgKSwNCiAgICBuYW1lID0gIiINCiAgKSAlPiUNCiAgbGF5b3V0KA0KICAgIHRpdGxlID0gIkNoZWNraW5nIEFtb3VudCwgQ3JlZGl0IFNjb3JlLCBhbmQgU2F2aW5ncyBBbW91bnQiLA0KICAgIHNjZW5lID0gbGlzdCgNCiAgICAgIHhheGlzID0gbGlzdCh0aXRsZSA9ICJDaGVja2luZyBBbW91bnQiKSwNCiAgICAgIHlheGlzID0gbGlzdCh0aXRsZSA9ICJDcmVkaXQgU2NvcmUiKSwNCiAgICAgIHpheGlzID0gbGlzdCh0aXRsZSA9ICJTYXZpbmdzIEFtb3VudCIpLA0KICAgICAgYXNwZWN0bW9kZSA9ICJjdWJlIg0KICAgICkNCiAgKQ0KDQojIE91dHB1dHRpbmcgcGxvdA0KRmVhdFJlbDA0DQpgYGANCg0K